package com.fu.consumer.exception;

import com.fu.core.common.BusinessException;
import com.fu.core.common.ExceptionCode;
import com.fu.core.common.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Map;
import java.util.Set;

/**
 * @author 付龙江
 * @date 2022/1/28 12:00
 */
@ControllerAdvice
@RestController
@Slf4j
public class ExceptionController {

    /**
     * 这个方法相当于Spring的AOP编程思想中后置抛出通知
     * 当异常发生时，Spring使用ControllerAdvice注解找到该Controller中的handler方法，捕获到的异常被注入到参数e中
     * @param e
     */
    @ExceptionHandler(BusinessException.class)
    public Map<String, Object> BusinessHandler(BusinessException e) {
        ExceptionCode exceptionCode = e.getExceptionCode();
        System.out.println(exceptionCode.getMessage());

        JsonResult json = new JsonResult();
        json.setCode(exceptionCode.getCode());
        json.setMsg(exceptionCode.getMessage());

        return json.getMap();
    }







    /**
     *
     * @param e
     * @param request
     * @return
     *
     * 和上面一样，都是异常处理，这里处理的是系统异常
     * 这里使用log4j2记录日志
     */
    @ExceptionHandler(Exception.class)
    public Map<String,Object> handler(Exception e, HttpServletRequest request) {
        e.printStackTrace();
        // 统异常记录到日志文件(slf4j)

        StringBuilder builder = new StringBuilder();
        //分割线方便阅读
        builder.append("\n" + "-----------------------begin-----------------------" + "\n");
        // 先记录异常对象的toString状态: 包括异常类型和异常原因
        builder.append(e);

        // 再遍历栈中的异常轨迹: 采用遍历方式
        StackTraceElement[] stackTrace = e.getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            builder.append(stackTraceElement);
        }
        //分割线方便阅读
        builder.append("\n" + "-----------------------end-----------------------");

        // 最后将builder写入日志文件
        // error/test/warn/info  都支持对象的直接写入日志文件, 会自动调用toString()方法
        log.error(builder.toString());

        // 给前端返回一个标准话术
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("500");
        jsonResult.setMsg("服务器开小差了,请稍后再试");
        return jsonResult.getMap();
    }

    /**
     *
     * @param errors
     * @return
     *
     * 处理第一种校验异常: 实体类中的字段校验不匹配
     * validation包中的BindException即为实体类中的字段校验不匹配异常
     * MethodArgumentNotValidException中定义了所有类型的不匹配信息
     * Error就是Spring自己封装的异常信息，包含了所有触发过的此类型的异常信息
     */
    @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})  // 注意导入的包是validation的
    public Map<String, Object> handleBindException(Errors errors){
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("0");
        StringBuilder stringBuilder = new StringBuilder();

        // 使用lambda表达式遍历所有出现过的异常（可能不止触发一个异常）
        errors.getAllErrors().forEach(error -> {
            // defaultMessage即为我们自己定义的message
            stringBuilder.append(error.getDefaultMessage() + ",");
        });
        jsonResult.setMsg(stringBuilder.toString());
        return jsonResult.getMap();
    }

    /**
     *
     * @param exception
     * @return
     *
     * 处理第二种校验异常: Controller方法参数校验规则不匹配
     * ConstraintViolationException即为Controller方法参数校验规则不匹配异常
     * ConstraintViolationException对象需要先调用getConstraintViolations方法才能取出异常信息
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Map<String, Object> handleConstraintViolationException(ConstraintViolationException exception) {
        exception.printStackTrace();
        StringBuilder errorInfo = new StringBuilder();
        String errorMessage ;

        Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
        for (ConstraintViolation<?> item : violations) {
            errorInfo.append(item.getMessage()).append(",");
        }

        errorMessage = errorInfo.toString().substring(0, errorInfo.toString().length()-1);
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("0");
        jsonResult.setMsg(errorMessage);
        return jsonResult.getMap();
    }
}

